package asteroids.model.programs.expressions;

import asteroids.model.Ship;

public class ExprDoubleEvaluator implements ExprEvaluator<Double> {

	public ExprDoubleEvaluator(Ship ship) {
		this.ship = ship;
		eee = new ExprEntityEvaluator(ship);
	}
	
	public Ship getShip() {
		return ship;
	}

	private final Ship ship;
	
	private ExprEntityEvaluator getEEE() {
		return eee;
	}
	
	private final ExprEntityEvaluator eee;
	

	@Override
	public Double evaluate(And and) {
		throw new IllegalArgumentException("&& operator cannot evaluate to a double at line "+and.getLine()+", column "+and.getColumn()+"!");
	}

	@Override
	public Double evaluate(Or or) {
		throw new IllegalArgumentException("|| operator cannot evaluate to a double at line "+or.getLine()+", column "+or.getColumn()+"!");
	}

	@Override
	public Double evaluate(LessThan lt) {
		throw new IllegalArgumentException("< operator cannot evaluate to a double at line "+lt.getLine()+", column "+lt.getColumn()+"!");
	}

	@Override
	public Double evaluate(GreaterThan gt) {
		throw new IllegalArgumentException("> operator cannot evaluate to a double at line "+gt.getLine()+", column "+gt.getColumn()+"!");
	}

	@Override
	public Double evaluate(Equality eq) {
		throw new IllegalArgumentException("== operator cannot evaluate to a double at line "+eq.getLine()+", column "+eq.getColumn()+"!");
	}

	@Override
	public Double evaluate(Add add) {
		double d1 = add.getExpr1().accept(this);
		double d2 = add.getExpr2().accept(this);
		return d1 + d2;
	}

	@Override
	public Double evaluate(Subtract sub) {
		double d1 = sub.getExpr1().accept(this);
		double d2 = sub.getExpr2().accept(this);
		return d1 - d2;
	}

	@Override
	public Double evaluate(Division div) {
		double d1 = div.getExpr1().accept(this);
		double d2 = div.getExpr2().accept(this);
		return d1 / d2;
	}

	@Override
	public Double evaluate(Multiply mul) {
		double d1 = mul.getExpr1().accept(this);
		double d2 = mul.getExpr2().accept(this);
		return d1 * d2;
	}

	@Override
	public Double evaluate(DoubleLiteral d) {
		return d.getD();
	}

	@Override
	public Double evaluate(BooleanLiteral b) {
		throw new IllegalArgumentException("BooleanLiteral cannot evaluate to a double at line "+b.getLine()+", column "+b.getColumn()+"!");
	}

	@Override
	public Double evaluate(Not not) {
		throw new IllegalArgumentException("! operator cannot evaluate to a double at line "+not.getLine()+", column "+not.getColumn()+"!");
	}

	@Override
	public Double evaluate(Sqrt sqrt) {
		double d = sqrt.getExpr().accept(this);
		return Math.sqrt(d);
	}

	@Override
	public Double evaluate(Sin sin) {
		double d = sin.getExpr().accept(this);
		return Math.sin(d);
	}

	@Override
	public Double evaluate(Cos cos) {
		double d = cos.getExpr().accept(this);
		return Math.cos(d);
	}

	@Override
	public Double evaluate(GetDirection getDir) {
		return getShip().getAngle();
	}

	@Override
	public Double evaluate(Self self) {
		throw new IllegalArgumentException("Self cannot evaluate to a double at line "+self.getLine()+", column "+self.getColumn()+"!");
	}

	@Override
	public Double evaluate(GetX getX) {
		return getX.getExpr().accept(getEEE()).getPosition().getX();
	}

	@Override
	public Double evaluate(GetY getY) {
		return getY.getExpr().accept(getEEE()).getPosition().getY();
	}
	
	@Override
	public Double evaluate(GetVX getVX) {
		return getVX.getExpr().accept(getEEE()).getVelocity().getX();
	}
	
	@Override
	public Double evaluate(GetVY getVY) {
		return getVY.getExpr().accept(getEEE()).getVelocity().getY();
	}
	
	@Override
	public Double evaluate(GetRadius getRadius) {
		return getRadius.getExpr().accept(getEEE()).getRadius();
	}

	@Override
	public Double evaluate(Variable var) {
		double d;
		try {
			d = getShip().getProgram().getAssignment(var.getName()).accept(this);
		}
		catch(IllegalArgumentException exc) {
			throw new IllegalArgumentException("Variable cannot evaluate to a double at line "+var.getLine()+", column "+var.getColumn()+"!", exc);
		}
		catch(NullPointerException exc) {
			throw new IllegalArgumentException("Encountered a null reference while trying to evaluate the variable at line "+var.getLine()+", column "+var.getColumn()+"!", exc);
		}
		return d;
	}

	@Override
	public Double evaluate(EntityLiteral e) {
		throw new IllegalArgumentException("EntityLiteral cannot evaluate to a double at line "+e.getLine()+", column "+e.getColumn()+"!");
	}

	@Override
	public Double evaluate(Null null1) {
		throw new IllegalArgumentException("Null cannot evaluate to a double at line "+null1.getLine()+", column "+null1.getColumn()+"!");
	}

}
